package core.helpers;

import io.appium.java_client.AppiumDriver;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.TestException;

import core.ApplicationManager;
import core.config.Config;

public class MobDriverHelper extends DriverHelper
{
	public static Process appiumProcess = null;
	private static AppiumDriver appium;

	/**
	 * Initializes MobDriverHelper
	 * @param p_manager
	 *            ApplicationManager instance
	 */
	public MobDriverHelper(ApplicationManager p_manager)
	{
		super(p_manager);
	}

	private List<String> executeCommand(String p_command, Boolean p_wait)
	{

		List<String> result = new ArrayList<String>();

		try
		{
			appiumProcess = Runtime.getRuntime().exec(p_command);

			if (p_wait)
			{
				appiumProcess.waitFor();

				BufferedReader reader =
						new BufferedReader(new InputStreamReader(appiumProcess.getInputStream()));

				String line = "";
				while ((line = reader.readLine()) != null)
				{
					result.add(line);
				}
			}

		}
		catch (Exception p_ex)
		{
			throw new TestException("Exception while trying to Execute CMD / Shell Command : " + p_command + " : " + p_ex);
		}

		return result;

	}

	private Integer getAdbDevicesCount()
	{
		Integer res = 0;

		return res;
	}

	// #rem node node_modules\appium\bin\appium --port 4723 --bootstrap-port 4725 -U emulator-5554 --chromedriver-port
	// 9515
	private void startAppiumServer()
	{

		String tmpCmdl = null;

		try
		{

			tmpCmdl = "adb devices";
			List<String> adbOut = executeCommand(tmpCmdl, true);
			Integer adbDevicesCount = 0;

			// in order to Avoid a Failure finding of a 'device' Key in ADBs output's First line, as a sub-string of
			// 'devices' word
			if ((adbOut != null) && (adbOut.size() > 1))
			{
				adbOut.remove(0);
			}

			String tmpAdbDevice = null;

			if (manager.testModel().getAppiumUdid().length() > 0)
			{
				tmpAdbDevice = manager.testModel().getAppiumUdid();
			}
			else
			{

				for (String adbLine : adbOut)
				{
					manager.log().info("ADB said : '" + adbLine + "'");
					if ((adbLine.contains("device")) || (adbLine.contains("emulator")))
					{
						adbDevicesCount++;
						tmpAdbDevice = adbLine.substring(0, adbLine.trim().indexOf('\t'));
						// break;
					}
				}
			}

			tmpCmdl = "adb.exe connect " + tmpAdbDevice;
			if (adbDevicesCount.equals(0))
			{
				manager.log().info("Seems ADB doesnt know any devices connected... Re-init ADB with an appropriate device : " + tmpCmdl);
				executeCommand(tmpCmdl, false);
			}

			if (tmpAdbDevice != null)
			{
				tmpCmdl = "cmd /c cd "
						+ manager.config().APPIUM_PATH
						+ " & cmd /c start node node_modules\\appium\\bin\\appium"
						+ " --port " + manager.testModel().getAppiumPort() // config().APPIUM_PORT
						+ " --bootstrap-port " + manager.testModel().getAppiumBootstrapPort() // manager.config().APPIUM_BOOTSTRAPPORT
						+ " -U " + manager.testModel().getAppiumUdid() // tmpAdbDevice
						+ " --chromedriver-port " + manager.testModel().getAppiumChromedriverPort(); // manager.config().APPIUM_CHROMEDRIVERPORT;

				manager.log().info("Trying to start Appium Server with Commandline : " + tmpCmdl);
				executeCommand(tmpCmdl, false);

				manager.log().info("Waiting Appium Server to start");
				Thread.sleep(24000);
			}
		}
		catch (Exception p_ex)
		{
			throw new TestException("Got Exception while trying to Initialize Appium Server : " + p_ex);
		}
	}

	/**
	 * Runs a local Android browser
	 */
	public WebDriver runLocalBrowser()
	{
		switch (manager.testModel().getServerType())
		{
			case Config.SERVER_LOCAL:
			{
				if (manager.testModel().getOS().equals(manager.config().OS_ANDROID))
				{
					try
					{

						manager.log().info("Appium settings :");
						manager.log().info("Device name: " + manager.config().APPIUM_DEVICE);
						manager.log().info("Browser name: " + manager.config().APPIUM_BROWSER);
						manager.log().info("Path to Appium folder" + manager.config().APPIUM_PATH);
						manager.log().info("Appium port: " + manager.testModel().getAppiumPort());
						manager.log().info("Appium boot start port: " + manager.testModel().getAppiumBootstrapPort());
						manager.log().info("Appium chromedriver: " + manager.config().APPIUM_CHROMEDRIVER);
						manager.log().info("Appium port: " + manager.testModel().getAppiumChromedriverPort());
						manager.log().info("Device id: " + manager.testModel().getAppiumUdid());
						manager.log().info("Appium log adress: " + manager.config().APPIUM_LOG);
						manager.log().info("Appium log level: " + manager.config().APPIUM_LOGLEVEL);
						manager.log().info("Appiuum command timeout: " + manager.config().APPIUM_COMMANDTIMEOUT);
						startAppiumServer();
						DesiredCapabilities capabilities = new DesiredCapabilities();

						// CAPS for Appium Mode - Working
						capabilities.setCapability("deviceName", "Android Emulator");
						capabilities.setCapability("platformName", "Android");
						capabilities.setCapability("automationName", "Appium");
						if (manager.testModel().getPlatform().equals(manager.config().PLATFORM_MOBILE))
						{
							capabilities.setCapability(CapabilityType.BROWSER_NAME, manager.config().APPIUM_BROWSER);
							capabilities.setCapability("app", "Browser");
						}
						else
						{
							capabilities.setCapability(CapabilityType.BROWSER_NAME, "");

							switch (manager.testModel().getSiteName())
							{
								case "benaughty":
								{
									capabilities.setCapability("appActivity", manager.config().APP_ACTIVITY_BN);
									capabilities.setCapability("androidPackage", manager.config().ANDROID_PACKAGE_BN);
									break;
								}
								case "wildbuddies":
								{
									capabilities.setCapability("appActivity", manager.config().APP_ACTIVITY_WB);
									capabilities.setCapability("androidPackage", manager.config().ANDROID_PACKAGE_WB);
									break;
								}
								case "flirt":
								{
									capabilities.setCapability("appActivity", manager.config().APP_ACTIVITY_FLIRT);
									capabilities.setCapability("androidPackage", manager.config().ANDROID_PACKAGE_FLIRT);
									break;
								}
								case "cheekylovers":
								{
									capabilities.setCapability("appActivity", manager.config().APP_ACTIVITY_CL);
									capabilities.setCapability("androidPackage", manager.config().ANDROID_PACKAGE_CL);
									break;
								}

								default:
								{
									manager.log().error("Incorrect site name specifyed for choosing the appActivity ana androidPackage");
								}
							}
						}
						capabilities.setCapability(CapabilityType.VERSION, "4.4");
						capabilities.setCapability(CapabilityType.PLATFORM, "WINDOWS");
						capabilities.setCapability("command-timeout", "120");
						capabilities.setCapability("newCommandTimeout", "999");
						capabilities.setCapability(CapabilityType.HAS_NATIVE_EVENTS, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_JAVASCRIPT, true);
						capabilities.setCapability(CapabilityType.HAS_TOUCHSCREEN, true);
						capabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
						capabilities.setCapability(CapabilityType.ENABLE_PERSISTENT_HOVERING, true);
						capabilities.setCapability(CapabilityType.ENABLE_PROFILING_CAPABILITY, true);
						capabilities.setCapability(CapabilityType.ROTATABLE, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_ALERTS, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_APPLICATION_CACHE, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_FINDING_BY_CSS, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_LOCATION_CONTEXT, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_SQL_DATABASE, true);
						capabilities.setCapability(CapabilityType.SUPPORTS_WEB_STORAGE, true);
						capabilities.setCapability(CapabilityType.TAKES_SCREENSHOT, true);
						URL tURL = new URL("http://127.0.0.1:" + manager.testModel().getAppiumPort() + "/wd/hub");

						appium = new AppiumDriver(tURL, capabilities);
						driver = appium;

						return driver;
					}
					catch (Exception p_ex)
					{
						throw new TestException("Cannot start a Web browser driver: " + p_ex);
					}

				}
			}
			case Config.SERVER_SAUCELABS:
			{
				try
				{
					return runRemoteBrowser();
				}
				catch (Exception p_ex)
				{

					throw new TestException("Cannot start a Web browser driver: " + p_ex);
				}
			}
		}

		return null;
	}

	/**
	 * Runs a remote web browser on SauceLabs
	 * @param p_os
	 *            OS name
	 * @param p_browser
	 *            Browser name
	 * @param p_browserVersion
	 *            Browser version
	 * @param p_proxy
	 *            Proxy
	 * @param p_jobName
	 *            Job name
	 */
	@SuppressWarnings("null")
	public void runRemoteBrowser(String p_os, String p_browser, String p_browserVersion, String p_proxy, String p_jobName)
	{
		DesiredCapabilities capabilities = null;// = getCapabilities(p_browser, p_proxy);

		// Set Browser Version
		if (p_browserVersion.equals("def"))
		{
			if (p_browser.equals(manager.config().BROWSER_ANDROID))
			{
				capabilities = getCapabilities(p_browserVersion, p_proxy);
				capabilities.setCapability("version", manager.config().BROWSER_ANDROID_VERSION);
				capabilities.setCapability("device-orientation", "portrait");

				capabilities.setCapability("command-timeout", 600);
				capabilities.setCapability("idle-timeout", 600);
			}
			else if (p_browser.equals(manager.config().BROWSER_IPAD))
			{
				capabilities = getCapabilities(p_browserVersion, p_proxy);
				capabilities.setCapability("version", manager.config().BROWSER_IPAD_VERSION);
				capabilities.setCapability("screen-resolution", manager.config().SCREEN_RESOLUTION_1024x768);
			}
		}
		else
		{
			capabilities.setCapability("version", p_browserVersion);
		}

		// Set Job name
		capabilities.setCapability("name", p_jobName);

		// Set OS
		if (p_os.equals(manager.config().OS_WIN_7))
		{
			capabilities.setCapability("platform", "Windows 7");
		}
		else if (p_os.equals(manager.config().OS_WIN_8))
		{
			capabilities.setCapability("platform", Platform.WIN8);
		}
		else if (p_os.equals(manager.config().OS_ANDROID))
		{
			capabilities.setCapability("platform", Platform.LINUX);
		}

		try
		{
			/*
			 * driver = new SwipeableWebDriverHelper(
			 * new URL("http://" + manager.testModel().getSaucelabsUsername() + ":" +
			 * manager.testModel().getSaucelabsKey()
			 * + "@ondemand.saucelabs.com:80/wd/hub"),
			 * capabilities);
			 */
		}

		catch (Exception ex)
		{
			throw new TestException(ex.toString());
		}
	}

	/**
	 * Runs a remote browser on SauceLabs
	 */
	public WebDriver runRemoteBrowser()
	{
		runRemoteBrowser(manager.testModel().getOS(),
				manager.testModel().getBrowserName(),
				manager.testModel().getBrowserVersion(),
				manager.testModel().getLocation(),
				manager.testModel().getPlatform() + ":"
						+ manager.testModel().getTestName() + ":"
						+ manager.testModel().getSiteName() + ":"
						+ manager.testModel().getLocation() + ":"
						+ manager.testModel().getBrowserName() + "_"
						+ manager.testModel().getBrowserVersion());

		return driver;
	}

	/**
	 * Get desired capabilities
	 * @param p_browser
	 * @return
	 */
	@SuppressWarnings("static-access")
	private DesiredCapabilities getCapabilities(String p_browser, String p_proxy)
	{
		DesiredCapabilities capabilities = null;

		if (p_browser.equals(manager.config().BROWSER_ANDROID))
		{
			capabilities = getAndroidOptions(p_proxy);
		}
		else if (p_browser.equals(manager.config().BROWSER_IPAD))
		{
			capabilities = getIPadOptions(p_proxy);
		}
		else if (p_browser.equals(manager.config().BROWSER_IPHONE))
		{
			capabilities = getIPhoneOptions(p_proxy);
		}

		return capabilities;
	}

	/**
	 * Gets iPad options
	 * @param p_proxy
	 *            Browser proxy
	 * @return DesiredCapabilities instance
	 */
	@SuppressWarnings("static-access")
	private DesiredCapabilities getIPadOptions(String p_proxy)
	{
		DesiredCapabilities capabilities = null;
		capabilities = new DesiredCapabilities().ipad();
		capabilities.setBrowserName("iPad");
		return capabilities;
	}

	/**
	 * Gets iPhone options
	 * @param p_proxy
	 *            Browser proxy
	 * @return DesiredCapabilities instance
	 */
	@SuppressWarnings("static-access")
	private DesiredCapabilities getIPhoneOptions(String p_proxy)
	{
		DesiredCapabilities capabilities = null;
		capabilities = new DesiredCapabilities().ipad();
		capabilities.setBrowserName("iPhone");
		return capabilities;
	}

	/**
	 * Gets Android options
	 * @param p_proxy
	 *            Browser proxy
	 * @return DesiredCapabilities instance
	 */
	@SuppressWarnings("static-access")
	private DesiredCapabilities getAndroidOptions(String p_proxy)
	{
		DesiredCapabilities capabilities = null;
		capabilities = new DesiredCapabilities().android();
		capabilities.setBrowserName("Android");
		return capabilities;
	}

	/**
	 * Used by mobile PO
	 * @param p_locator
	 */
	public void submit(By p_locator)
	{
		manager.log().info("Submit Form via an element => [" + p_locator + "]");

		WebElement lElement = null;

		try
		{
			if (this.isElementDisplayed(p_locator))
			{

				scrollTo(p_locator);

				lElement = driver.findElement(p_locator);

				lElement.submit();
			}
		}
		catch (Exception p_ex)
		{
			throw new TestException("Cannot Submit a Form via an element => " + p_locator + "\nReason: " + p_ex);

		}
	}

	/**
	 * Hide keyboard on android device
	 */
	public void hideKeyboardApp()
	{
		appium.hideKeyboard();
	}

	/**
	 * Get coordinates of the screen and scroll from start x,y to the and x,y coordinates
	 */
	public void swipe()
	{
		try
		{
			int startx = 0;
			double get_start_y = appium.manage().window().getSize().getHeight() * 0.85;
			int starty = (int) get_start_y;
			int endx = 0;
			double end_start_y = appium.manage().window().getSize().getHeight() * 0.15;
			int endy = (int) end_start_y;
			int duration = 500;
			appium.swipe(startx, starty, endx, endy, duration);
		}
		catch (Exception p_ex)
		{
			throw new TestException("Cannot swipe with given coordinates: " + p_ex);
		}
	}

	/**
	 * Perform tap of the element (pass 1as x and y for default)
	 */
	public void tap(int x, WebElement p_element, int y)
	{
		appium.tap(x, p_element, y);
	}
}